package terminal

import (
	"context"
	"device-admin/config/mysql"
	"device-admin/internal/app/grpc/client"
	"device-admin/internal/app/grpc/protoc/advertisingc"
	"device-admin/internal/app/grpc/protoc/devicec"
	"device-admin/internal/app/grpc/service/pubsub"
	"device-admin/internal/models"
	"encoding/base64"
	"encoding/json"
	"strconv"
	"strings"
	"time"

	"192.168.1.75/go-pkg/helper"
	"192.168.1.75/go-pkg/logx"
	"github.com/samber/lo"
	"google.golang.org/protobuf/proto"
)

var TerminalTypeMap = map[string]int32{
	"97": 1,
	"98": 2,
}

// 升级中
// status 1 同步中 2 同步失败 3 同步成功
func AdvertisingUpdateStatus(robotID, terminalType, ScheduleID string, status int) {
	AdvertisingClient, connErr := client.GetAdvertisingClient()
	if connErr != nil {
		logx.Error(context.Background(), "广告系统连接错误")
		return
	}

	var params = &advertisingc.PublishScheduleCallbackReq{}
	params.RobotName = robotID
	params.TerminalType = int32(TerminalTypeMap[terminalType])
	params.Status = int32(status)
	params.ScheduleId = ScheduleID
	rsp, rspErr := AdvertisingClient.PublishScheduleCallback(context.Background(), params)
	if rspErr != nil || rsp.Code != 0 {
		logx.Error(context.Background(), "广告投放回执错误", logx.Any(rsp.Msg, rsp.Code))
		return
	}
}

// 设备离线 广告投放状态处理
func AdvertisingDeviceOffline(robotID, terminalType string) {
	AdvertisingClient, connErr := client.GetAdvertisingClient()
	if connErr != nil {
		logx.Error(context.Background(), "广告系统连接错误")
		return
	}

	var params = &advertisingc.PublishScheduleCallbackReq{}
	params.RobotName = robotID
	params.TerminalType = int32(TerminalTypeMap[terminalType])
	params.Status = 0
	rsp, rspErr := AdvertisingClient.PublishScheduleCallback(context.Background(), params)
	if rspErr != nil || rsp.Code != 0 {
		logx.Error(context.Background(), "广告投放回执错误", logx.Any("rsp", rsp))
		return
	}
}

// SendAdvertisingFileContent 发送广告素材内容
func SendAdvertisingFileContent(robotID, terminalType string, content []byte, totalSize int64, seq string, err error) error {
	rspTransmissionFile := &devicec.AdvertisingTransmissionFileRsp{}
	if err != nil {
		rspTransmissionFile.Status = -1
	}
	rspTransmissionFile.Content = content
	rspTransmissionFile.Md5 = helper.Md5Bytes(content)
	rspTransmissionFile.TotalSize = totalSize
	msgFullName := string(rspTransmissionFile.ProtoReflect().Descriptor().FullName())
	bytes, _ := proto.Marshal(rspTransmissionFile)
	if terminalType == "97" || terminalType == "98" {
		pubsub.LocalMsgPublish(robotID, terminalType, "msg", msgFullName, bytes, seq)
	}
	return nil
}

func MakeCMDMsg(protoMessage proto.Message) string {
	msg := &devicec.Msg{}
	msg.Ack = 0
	msg.Seq = helper.RandString(16)
	msg.Name = string(protoMessage.ProtoReflect().Descriptor().FullName())
	msg.Content, _ = proto.Marshal(protoMessage)
	// protobuf序列化
	bytes, _ := proto.Marshal(msg)
	return base64.StdEncoding.EncodeToString(bytes)
}

// 设备上报播放次数
func AdvertisingShowCount(robotID, terminalType, ScheduleId string, Count int32) {
	AdvertisingClient, connErr := client.GetAdvertisingClient()
	if connErr != nil {
		logx.Error(context.Background(), "广告系统连接错误")
		return
	}

	var params = &advertisingc.AdvertisingShowCountReq{}
	params.ScheduleId = ScheduleId
	params.RobotName = robotID
	params.TerminalType = int32(TerminalTypeMap[terminalType])
	params.Count = Count
	rsp, rspErr := AdvertisingClient.AdvertisingShowCount(context.Background(), params)
	if rspErr != nil || rsp.Code != 0 {
		logx.Error(context.Background(), "设备上报播放次数错误", logx.Any(rsp.Msg, rsp.Code))
		return
	}
}

// 获取投放计划并推送
// devices:  053_1,053_2
// force == true  强制推送
// force == false 常规推送
func GetPublishSchedule(devices string, force bool, debug string) (deviceCount int) {
	AdvertisingClient, connErr := client.GetAdvertisingClient()
	if connErr != nil {
		logx.Error(context.Background(), "广告系统连接错误")
		return
	}
	// 当前在线的设备
	var TerminalList = []models.Terminal{}
	mysql.NewDB().Model(&models.Terminal{}).
		Where("latest_active_time > ?", time.Now().Unix()-60).
		Select("robot_id, terminal_type").
		Find(&TerminalList)
	var onLineDeviceList = []string{}
	var TerminalTypeMap1 = map[string]string{
		"97": "1",
		"98": "2",
	}
	for _, v := range TerminalList {
		if _, ok := TerminalTypeMap1[v.TerminalType]; ok {
			onLineDeviceList = append(onLineDeviceList, v.RobotID+"_"+TerminalTypeMap1[v.TerminalType])
			if v.TerminalType == "97" {
				onLineDeviceList = append(onLineDeviceList, v.RobotID+"_3")
			}
		}
	}
	inputDevices := devices
	devices_arr := strings.Split(devices, ",")
	for _, device := range devices_arr {
		device_sp := strings.Split(device, "_")
		if device_sp[1] == "3" {
			if !lo.Contains(devices_arr, device_sp[0]+"_1") {
				devices_arr = append(devices_arr, device_sp[0]+"_1") // 053_3
			}
		}
		if device_sp[1] == "1" {
			if !lo.Contains(devices_arr, device_sp[0]+"_3") {
				devices_arr = append(devices_arr, device_sp[0]+"_3") // 053_3
			}
		}
	}
	devices = strings.Join(devices_arr, ",")

	// 强制推送时先判断是否在线
	if force {
		searchDevices := lo.Intersect(onLineDeviceList, strings.Split(devices, ","))
		devices = strings.Join(searchDevices, ",")
	}
	// 获取计划信息
	infoReq := &advertisingc.GetPublishScheduleReq{}
	infoReq.Devices = devices // 在线设备
	infoReq.Force = force
	rsp, err := AdvertisingClient.GetPublishSchedule(context.Background(), infoReq)

	if err != nil {
		logx.Error(context.Background(), "GetPublishSchedule err", logx.Any("rsp", rsp))
		return
	}
	// 下发设备
	var TerminalTypeMap = map[int32]string{
		1: "97",
		2: "98",
		3: "97",
	}
	PushDevice := []string{}
	PushDevice = devices_arr

	if rsp.Code == 0 {
		// 组装设备的投放计划信息
		var DeviceMaterialMap = make(map[string][]*devicec.AdvertisingPublishReq_ScheduleMaterial) // 053_1 => []
		if len(rsp.List) != 0 {                                                                    // 有计划
			for _, v := range rsp.List {
				if !lo.Contains(PushDevice, v.RobotName+"_"+strconv.Itoa(int(v.TerminalType))) {
					PushDevice = append(PushDevice, v.RobotName+"_"+strconv.Itoa(int(v.TerminalType))) // 053_1

					if v.TerminalType == 1 {
						if !lo.Contains(PushDevice, v.RobotName+"_3") {
							PushDevice = append(PushDevice, v.RobotName+"_3") // 053_3
						}
					}
					if v.TerminalType == 3 {
						if !lo.Contains(PushDevice, v.RobotName+"_1") {
							PushDevice = append(PushDevice, v.RobotName+"_1") // 053_3
						}
					}
				}
				for _, m := range v.Materials {
					var item = &devicec.AdvertisingPublishReq_ScheduleMaterial{}
					item.FilePath = m.FilePath
					item.FileMd5 = m.FileMd5
					item.FileSize = m.FileSize
					item.Duration = m.Duration
					item.RepeatTimes = m.RepeatTimes
					item.Position = m.Position
					item.ScheduleId = m.ScheduleId
					item.Gender = m.Gender
					item.Age = m.Age
					item.Tag = m.Tag
					item.Details = ""
					item.ScheduleType = m.ScheduleType
					item.PublishCount = m.PublishCount
					item.DetailsShow = m.DetailsShow
					item.DetailsUrl = "/h5-page/page97/index.html#/pages/detail/advertising?schedule_id=" + m.ScheduleId
					item.MaterialId = m.MaterialId
					item.PlayType = m.PlayType
					item.RegularInterval = m.RegularInterval
					DeviceMaterialMap[v.RobotName+"_"+TerminalTypeMap[v.TerminalType]] = append(DeviceMaterialMap[v.RobotName+"_"+TerminalTypeMap[v.TerminalType]], item)
				}
			}
		}
		oldPushDevice := PushDevice
		PushDevice = lo.Intersect(PushDevice, strings.Split(devices, ","))

		logx.Info(context.Background(), "advertising GetPublishSchedule pushDevice", logx.Any("pushDevice", PushDevice))
		logx.Info(context.Background(), "advertising GetPublishSchedule inputDevices", logx.String("inputDevices", inputDevices))
		logx.Info(context.Background(), "advertising GetPublishSchedule devices", logx.String("devices", devices))
		logx.Info(context.Background(), "advertising GetPublishSchedule onLineDeviceList", logx.String("onLineDeviceList", strings.Join(onLineDeviceList, ",")))
		logx.Info(context.Background(), "advertising GetPublishSchedule oldPushDevice", logx.String("oldPushDevice", strings.Join(oldPushDevice, ",")))
		logx.Info(context.Background(), "advertising GetPublishSchedule force debug", logx.Bool("force", force), logx.String("debug", debug))
		for _, robotTerminal := range PushDevice {
			robotTerminals := strings.Split(robotTerminal, "_") // 053_1
			robotID := robotTerminals[0]
			terminalType, _ := strconv.Atoi(robotTerminals[1]) // int 1
			reqPublishSchedule := &devicec.AdvertisingPublishReq{}
			reqPublishSchedule.RobotName = robotID                                                // 设备
			if _, ok := DeviceMaterialMap[robotID+"_"+TerminalTypeMap[int32(terminalType)]]; ok { // 有素材
				reqPublishSchedule.Materials = DeviceMaterialMap[robotID+"_"+TerminalTypeMap[int32(terminalType)]] // 素材
			} else { // 没有素材
				reqPublishSchedule.Materials = []*devicec.AdvertisingPublishReq_ScheduleMaterial{}
			}
			sendMsg := MakeCMDMsg(reqPublishSchedule)
			// 发送消息
			if force {
				if strings.Contains(inputDevices, reqPublishSchedule.RobotName) {

					logx.Info(context.Background(), "advertising GetPublishSchedule", logx.String("robot_id", robotID), logx.Int("terminal_type", terminalType), logx.Any("reqPublishSchedule", func() string {
						bytes, _ := json.Marshal(reqPublishSchedule)
						return string(bytes)
					}()), logx.Int("reqPublishSchedule count:", len(reqPublishSchedule.Materials)), logx.String("debug", debug))

					pubsub.CMDPublish(robotID, TerminalTypeMap[int32(terminalType)], "msg", sendMsg)

				}
			} else {
				if len(reqPublishSchedule.Materials) > 0 {
					logx.Info(context.Background(), "advertising GetPublishSchedule", logx.String("robot_id", robotID), logx.Int("terminal_type", terminalType), logx.Any("reqPublishSchedule", func() string {
						bytes, _ := json.Marshal(reqPublishSchedule)
						return string(bytes)
					}()), logx.Int("reqPublishSchedule count:", len(reqPublishSchedule.Materials)), logx.String("debug", debug))

					pubsub.CMDPublish(robotID, TerminalTypeMap[int32(terminalType)], "msg", sendMsg)
				}
			}

		}
	}
	return len(PushDevice)
}

// 播放清单上报
func UploadAdvertisingPlayList(robotID, terminalType string, materialList []*devicec.AdvertisingPlayListReq_PlayListMaterial) {
	// 1.回复
	req := &devicec.AdvertisingPlayListRsp{}
	req.Code = 0 // 0 成功
	sendMsg := MakeCMDMsg(req)
	bytes, _ := json.Marshal(sendMsg)
	logx.Info(context.Background(), "AdvertisingPlayListRsp", logx.String("robot_id", robotID), logx.String("terminal_type", terminalType), logx.Any("sendMsg", string(bytes)))
	if terminalType == "97" || terminalType == "98" {
		msgFullName := string(req.ProtoReflect().Descriptor().FullName())
		pubsub.LocalMsgPublish(robotID, terminalType, "msg", msgFullName, bytes, "")
	}

	// 2.推送播放清单至广告系统
	AdvertisingClient, connErr := client.GetAdvertisingClient()
	if connErr != nil {
		logx.Error(context.Background(), "广告系统连接错误")
		return
	}
	logx.Info(context.Background(), "advertising materialList", logx.String("robot_id", robotID), logx.String("terminal_type", terminalType), logx.Any("materialList", materialList))
	var materials = []*advertisingc.AdvertisingPlayListUploadReq_PlayListMaterial{}
	for _, v := range materialList {
		item := &advertisingc.AdvertisingPlayListUploadReq_PlayListMaterial{
			ScheduleId: v.ScheduleId,
			MaterialId: v.MaterialId,
			FilePath:   v.FilePath,
			FileMd5:    v.FileMd5,
		}
		materials = append(materials, item)
	}
	var params = &advertisingc.AdvertisingPlayListUploadReq{}
	params.RobotName = robotID
	params.TerminalType = int32(TerminalTypeMap[terminalType])
	params.Materials = materials

	rsp, rspErr := AdvertisingClient.AdvertisingPlayListUpload(context.Background(), params)
	if rspErr != nil {
		logx.Error(context.Background(), "广告播放清单上报错误", logx.Any("rsp", rsp))
		return
	}
}
